package ie.lawlor.amvc.patterns.table.swing;

import ie.lawlor.amvc.event.Event;
import ie.lawlor.amvc.event.PopupPayload;
import ie.lawlor.amvc.patterns.table.TableCellReference;
import ie.lawlor.amvc.patterns.table.TableController;
import ie.lawlor.amvc.patterns.table.TableModel;
import ie.lawlor.amvc.swing.JPanelView;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;

import org.apache.log4j.Logger;



public class JTableView extends JPanelView {
  
  private SwingTableModel swingTableModel;
  private javax.swing.JScrollPane scrollPane;
  private JPanel buttonPanel;
  private javax.swing.JTable table;
  private TitledBorder border;
  public JTableView(String name) {
    super(name);
    logger = Logger.getLogger(getLoggerName());
    initComponents();
    initTable();
  }
  
  protected String getBorderTitle(){
    return border.getTitle();
  }
  
  public void setBorderTitle(String borderTitle) {
    border.setTitle(borderTitle);
  }
  
  private void initTable(){
    logger.debug("Entering");
    table.getSelectionModel().addListSelectionListener(
            new ListSelectionListener(){
              public void valueChanged(ListSelectionEvent e) {
                if (!updatingView && !e.getValueIsAdjusting()) {
                  logger.info("Firing item selected");
                  fire(new Event(getController().getEvent(TableController.SELECT_EVENT), 
                          this, 
                          new TableCellReference(table.getSelectedRow(), table.getSelectedColumn())));
                }
              }
            });
    
    ListSelectionModel columnSelectionModel =
      table.getColumnModel().getSelectionModel();
    columnSelectionModel.addListSelectionListener(
            new ListSelectionListener() {
              public void valueChanged(ListSelectionEvent e) {
                if (!updatingView && !e.getValueIsAdjusting()) {
                  logger.info("Firing item selected");
                  fire(new Event(getController().getEvent(TableController.SELECT_EVENT), 
                          this, 
                          new TableCellReference(table.getSelectedRow(), table.getSelectedColumn())));
                }
              }
            });
    
    table.addMouseListener(new MouseListener(){
      public void mousePressed(MouseEvent e){};
      public void mouseReleased(MouseEvent e){};
      public void mouseEntered(MouseEvent e){};
      public void mouseExited(MouseEvent e){};
      
      public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() > 1) {
          fire(new Event(getController().getEvent(TableController.OPEN_EVENT), 
                  this, 
                  new TableCellReference(table.getSelectedRow(), table.getSelectedColumn())));
        }
      };
    });
    
    // TODO Deal with multiple selection
    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    
    //TODO Implement column listener for sorting
//  table.getTableHeader().addMouseListener(
//  new ListColumnListener(table.getColumnModel(), this));
  }
  
  public void doUpdateView(Event e){
    logger.debug("Entering");
    updatingView = true;
    TableModel model = (TableModel)e.getPayload();
    refreshTable(model);
    initColumnSizes(model);
    if (model.getSelectedRow()>=0){
      table.setRowSelectionInterval(model.getSelectedRow(),model.getSelectedRow());
    }
    if (model.getSelectedColumn() >= 0){
      table.setColumnSelectionInterval(model.getSelectedColumn(),model.getSelectedColumn());
    }
    updatingView=false;
    logger.debug("Exiting");
  }
  
  private void refreshTable(TableModel model){
    logger.debug("Entering");
    table.setModel(getTableModel(model));
//  table.updateUI();
    table.tableChanged(new TableModelEvent(
            getTableModel(model), 
            TableModelEvent.HEADER_ROW, 
            getTableModel(model).getRowCount()-1, 
            TableModelEvent.ALL_COLUMNS));
  }
  
  /**
   * This method initializes good column sizes.
   *
   * TODO Should use the font width to calculate the correct column width.
   */
  private void initColumnSizes(TableModel model) {
    TableColumn column = null;
    /*
     Font font = docTable.getCellRenderer(0,0).getTableCellRendererComponent(docTable, "x", false, false, 0,0).getFont();
     FontMetrics fm = docTable.getCellRenderer(0,0).getTableCellRendererComponent(docTable, "x", false, false, 0,0).getFontMetrics(font);
     int fontWidths[] = fm.getWidths();
     */
    int noOfColumns = table.getColumnCount();
    for (int i = 0; i < noOfColumns; i++) {
      column = table.getColumnModel().getColumn(i);
      
      column.setPreferredWidth(model.getPreferredWidth(i)*10);
      column.setMaxWidth(model.getMaxWidth(i)*10);
    }
  } 
  
  private void initComponents() {
    logger.debug("Entering");
    scrollPane = new javax.swing.JScrollPane();
    table = new javax.swing.JTable();
    setLayout(new java.awt.BorderLayout());
    setBackground(java.awt.Color.white);
    border = new javax.swing.border.TitledBorder(null, "", javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Arial", 0, 12), new java.awt.Color(102, 102, 255));
    scrollPane.setBorder(border);
    scrollPane.setBackground(java.awt.Color.white);
    scrollPane.setHorizontalScrollBarPolicy(javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    table.setShowVerticalLines(false);
    table.setShowHorizontalLines(false);
    table.setGridColor(java.awt.Color.gray);
    table.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseReleased(java.awt.event.MouseEvent evt) {
        tableMouseReleased(evt);
      }
    });
    scrollPane.setViewportView(table);
    add(scrollPane, java.awt.BorderLayout.CENTER);
    buttonPanel = new JPanel();
    JButton refreshButton = new JButton("Refresh");
    refreshButton.addActionListener(new ActionListener(){
      public void actionPerformed(ActionEvent e){
        fire(new Event(getController().getEvent(TableController.REFRESH_EVENT), 
                this));
      }
    });
    buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
    buttonPanel.add(refreshButton);
    add(buttonPanel, java.awt.BorderLayout.NORTH);
  }
  
//TODO Implement simpler popup event at standard level.
  private void tableMouseReleased(java.awt.event.MouseEvent evt) {
    // If the event is a right click, fire a popup menu event
    logger.debug("Mouse clicked on table");
    if (evt.isPopupTrigger()) {
      int row = table.rowAtPoint(evt.getPoint());
      int column = table.columnAtPoint(evt.getPoint());
      fire(new Event(getController().getEvent(TableController.SELECT_EVENT), 
              this, 
              new TableCellReference(row, column)));
      fire(getController().getEvent(TableController.POPUP),
              new PopupPayload(evt.getComponent(), 
                      evt.getPoint()));
    }
  }
  
  /** Allows the logger name to be set by derived class
   *
   */
  protected String getLoggerName() {
    return "hmvc.list.view";
  }    

  
  protected AbstractTableModel getTableModel(TableModel model){
    logger.debug("Entering");
    if (swingTableModel == null){
      swingTableModel = new SwingTableModel(model);
      table.setModel(swingTableModel);
    }
    logger.debug("Exiting");
    return swingTableModel;
  }
  
  protected class SwingTableModel extends AbstractTableModel {
    /**
     * Comment for <code>serialVersionUID</code>
     */
    private static final long serialVersionUID = 1L;
    TableModel model;
    public SwingTableModel(TableModel model){
      this.model = model;
    }
    public int getRowCount() {
      return model.getNumberOfRows();
    }
    public int getColumnCount() {
      return model.getNumberOfFields();
    }
    
    public Object getValueAt(int x, int y){
      return model.getValueAt(x, y);
    }
    public String getColumnName(int columnNum) {
      return model.getFieldName(columnNum);
    }
    
  };    
}
